using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Oops.Xna.Framework.UI;
using Microsoft.Xna.Framework.Input;

namespace Oops.Xna.Framework.Graphics
{
	[Flags()]
	public enum TextFormatFlags
	{
		Default = 0,
		Left = 0,
		Top = 0,
		HorizontalCenter = 1,
		Right = 2,
		VerticalCenter = 4,
		Bottom = 8,

		WordBreak = 0x10,
		SingleLine = 0x20,
		ExpandTabs = 0x40,

		PathEllipsis = 0x4000,
		EndEllipsis = 0x8000,

		RightToLeft = 0x20000,
		WordEllipsis = 0x40000,
	}

	// TODO : NOT COMPLETE!!!
	public static class SpriteFontExtension
	{
		#region TextJustification Declaration

		private enum TextJustification
		{
			Left,
			Center,
			Right
		}

		#endregion

		public static Vector2 MeasureText(string text, SpriteFont font, Vector2 proposedSize, TextFormatFlags flags)
		{
			// Assert parameters.
			Debug.Assert(!String.IsNullOrEmpty(text));
			Debug.Assert(font != null);

			// Create and process the lines of the text.
			List<string> lines = new List<string>();
			return SpriteFontExtension.GetLines(text, font, proposedSize, flags, ref lines);
		}

		public static void DrawText(
			SpriteBatch spriteBatch,
			string text,
			SpriteFont font,
			Rectangle bounds,
			Color color,
			TextFormatFlags flags)
		{
			// Assert parameters.
			Debug.Assert(spriteBatch != null);
			Debug.Assert(!String.IsNullOrEmpty(text));
			Debug.Assert(font != null);

			// Create and process the lines of the text.
			List<string> lines = new List<string>();
			Vector2 size = SpriteFontExtension.GetLines(text, font, new Vector2(bounds.Width, bounds.Height), flags, ref lines);

			// Determine justification.
			TextJustification justification = TextJustification.Left;
			if ((flags & TextFormatFlags.HorizontalCenter) == TextFormatFlags.HorizontalCenter)
			{
				justification = TextJustification.Center;
			}
			else if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right)
			{
				justification = TextJustification.Right;
			}

			// Determine where the first lines should start drawing.
			Vector2 position = new Vector2(bounds.X, bounds.Y);
			if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter)
			{
				position.Y = bounds.Y + (bounds.Height * 0.5f) - (size.Y * 0.5f);
			}
			else if ((flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom)
			{
				position.Y = bounds.Bottom - size.Y;
			}

			// Iterate on all the lines.
			Vector2 lineSize;
			for (int i = 0; i < lines.Count; i++)
			{
				// Get the current line.
				string line = lines[i];

				// Determine the the horizontal position of the line.
				switch (justification)
				{
					case TextJustification.Left:
						position.X = bounds.X;
						break;

					case TextJustification.Center:
						lineSize = font.MeasureString(line);
						position.X = bounds.X + (bounds.Width * 0.5f) - (lineSize.X * 0.5f);
						break;

					case TextJustification.Right:
						lineSize = font.MeasureString(line);
						position.X = bounds.Right - lineSize.X;
						break;
				}

				// Draw the current line of text.
				spriteBatch.DrawString(font, line, position, color);

				// Move position to the next line.
				position.Y += font.LineSpacing;
			}
		}

		private static Vector2 GetLines(string text, SpriteFont font, Vector2 proposedSize, TextFormatFlags flags, ref List<string> lines)
		{
			// Assert parameters.
			Debug.Assert(!String.IsNullOrEmpty(text));
			Debug.Assert(font != null);
			Debug.Assert(lines != null);

			// Expand tabs if necessary.
			if ((flags & TextFormatFlags.ExpandTabs) == TextFormatFlags.ExpandTabs)
			{
				text = text.Replace(((char)9).ToString(), "        ");
			}

			// Just measure string if single line processing.
			if ((flags & TextFormatFlags.SingleLine) == TextFormatFlags.SingleLine)
			{
				lines.Add(text);
				return font.MeasureString(text);
			}

			// Get the lines of the text.
			lines.AddRange(text.Replace(Environment.NewLine, ((char)13).ToString()).Split((char)13));

			// Initialize the result.
			Vector2 result = Vector2.Zero;

			if ((flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak)
			{
				// Measure the ellipsis with the current font.
				Vector2 ellipsisSize = font.MeasureString("...");

				int index = 0;
				bool lineInserted = false;
				while (index < lines.Count)
				{
					// Get the line of text at the current index.
					string line = lines[index];

					// Measure the line of text.
					Vector2 size = font.MeasureString(line);

					// Check for word-wrap.
					if (size.X > proposedSize.X)
					{
						// Determine if a trailing space exists.
						string trailingSpace = string.Empty;
						if (line.EndsWith(" "))
						{
							trailingSpace = " ";
							line = line.TrimEnd();
						}

						// Get the index of the last space character.
						int lastSpaceIndex = line.LastIndexOf(" ");
						if (lastSpaceIndex != -1)
						{
							// Get the last word of the current line.
							string lastWord = line.Substring(lastSpaceIndex + 1);

							// Start a new line if the current line being processed is the last line.
							if (index == lines.Count - 1)
							{
								lines.Add(lastWord);
								lineInserted = true;
							}
							else
							{
								// A line was inserted on the last iteration.
								if (lineInserted)
								{
									// Add the last word to the beginning of the next line.
									lines[index + 1] = lastWord + trailingSpace + lines[index + 1];
								}
								else
								{
									// Start a new line with the last word below the current line being processed.
									lines.Insert(index + 1, lastWord);
									lineInserted = true;
								}
							}

							// Remove the last word from the current line being processed.
							lines[index] = line.Substring(0, lastSpaceIndex + 1);
						}
						else
						{
							// No spaces exist in the current line being processed.
							lineInserted = false;

							// Update the height.
							result.Y += font.LineSpacing;

							// Go to the next line.
							index++;
						}
					}
					else
					{
						// The current line being processed fits in the proposed size.
						lineInserted = false;

						// Update the width.
						if (size.X > result.X)
						{
							result.X = size.X;
						}

						//if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis)
						//{
						//    if (result.Y + font.LineSpacing > proposedSize.Y)
						//    {
						//        // Remove the last word from the current line being processed.
						//        lines[index] = line.Substring(0, lastSpaceIndex);

						//        // Add ellipsis
						//        lines[index] += "...";

						//        // Exit the loop.
						//        break;
						//    }
						//}

						// Update the height.
						result.Y += font.LineSpacing;

						// Go to the next line.
						index++;
					}
				}
			}

			// Returns the size of the text.
			return result;
		}

		public static float GetMaximumHeight(SpriteFont font, float offset)
		{
			return font.MeasureString("Wg").Y + offset;
		}
	}
}